package com.kreig133.spine.simplemodel;

import com.kreig133.spine.abstractmodel.AbstractExperiment;
import com.kreig133.spine.abstractmodel.AbstractSpine;
import com.kreig133.spine.abstractmodel.AbstractState;
import com.kreig133.spine.abstractmodel.AbstractVelebra;
import com.kreig133.spine.interfaces.ExperimentResultSaver;
import com.kreig133.spine.interfaces.Velebra;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author C.C.-fag
 */
public class SimpleSpine extends AbstractSpine {

    protected AbstractState[] previousStates;
    protected AbstractState[] currentStates;
    protected SimpleExperiment experiment;

    @Override
    public void addState(AbstractVelebra velebra, AbstractState state) {
        assert velebrae.indexOf(velebra) < currentStates.length;
        currentStates[velebrae.indexOf(velebra)] = state;
    }

    @Override
    public SimpleEnvironment getEnvironment(AbstractVelebra velebra) {
        int index = velebrae.indexOf(velebra);
        checkPrevious();
        SimpleEnvironment res = new SimpleEnvironment(
                previousStates[index - 1],
                index < (velebrae.size() - 1) ? previousStates[index + 1] : null,
                experiment.getStep());

        return res;
    }

    @Override
    public void doExperiment(AbstractExperiment e, ExperimentResultSaver saver) {
        this.saver = saver;
        this.previousStates = new SimpleState[velebrae.size()];
        this.currentStates = new SimpleState[velebrae.size()];
        this.result = new HashMap<Double, List<AbstractState>>();
        this.experiment = (SimpleExperiment) e;
        (new ExperimentThread()).start();
    }

    public void checkPrevious() {
        for (AbstractState state : previousStates) {
            if (state == null) {
                throw new NullPointerException("Ошибка! перделывай");
            }
//            System.out.println("Все пучком");
        }
    }

    @Override
    public void setVelebrae(List<Velebra> velebrae) {
        this.velebrae = velebrae;
    }

    @Override
    public Map<Double, List<AbstractState>> getExperimentResult() {
        return result;
    }

    private class ExperimentThread extends Thread {

        SimpleSpine ss = SimpleSpine.this;

        @Override
        public void run() {
            boolean nextStep = false;
            List<SimpleEvent> events = new ArrayList<SimpleEvent>();
            for (Velebra velebra : velebrae) {
                Thread thread = new Thread(velebra);
                thread.setDaemon(true);
                thread.start();
            }

            while (ss.currentTime < experiment.getTime()) {
                nextStep = false;
                metka:
                while (!nextStep) {
                    this.yield();
                    for (AbstractState state : currentStates) {
                        if (state == null) {
                            continue metka;
                        }
                    }
                    nextStep = true;
                }
                //сохраняем текущие состояния позвонокв 

                previousStates = currentStates;
                result.put(currentTime, Arrays.asList(currentStates));

                //Получаем актуальные события
                SimpleEvent event = experiment.getNextEvent();
                if (event != null) {
                    events.add(event);
                }

                List<SimpleEvent> temp = new ArrayList<SimpleEvent>();
                //обрабатываем списко текущих событий
                if (!events.isEmpty()) {
                    for (SimpleEvent ev : events) {
                        if (ev.check(currentTime) && !ev.done(currentTime)) {
//                            assert (previousStates[0].getCoordinate()
//                                    + event.getShift() / event.getTimeLength() * experiment.getSimulationStep()) > 0 : "КОСЯК!";
                            ((Basement) velebrae.get(0)).getState().setCoordinate(
                                    previousStates[0].getCoordinate()
                                    + ev.getShift()
                                    / ev.getTimeLength()
                                    * experiment.getStep());
//                            try{
//                                System.out.println("НАДО же");
//                                Thread.sleep(10000);
//                            }catch(Exception e){                               
//                            }


                        } else if (ev.done(currentTime)) {
                            temp.add(ev);
                        }
                    }
                }

                for (SimpleEvent ev : temp) {
                    events.remove(ev);
                }


                currentStates = new SimpleState[previousStates.length];
                StringBuilder sb = new StringBuilder();
                synchronized (forSynchronize) {

                    sb.append("CURRENT TIME = ");
                    sb.append(String.format("%.3f", currentTime ));
                    sb.append("; COORDINATES - ");
                    for (AbstractState state : previousStates) {
                        sb.append("  ");
                        sb.append(state.getCoordinate());
                    }
                    sb.append("\n");
                    System.out.println(sb);
                    forSynchronize.notifyAll();
                }

                currentTime += experiment.getStep();
                //Logger.getLogger(SimpleSpine.class.getName()).log(Level.SEVERE, "11111111111111111111111111111111111111111111");
//                Message e = new NotifyDescriptor.Message("dlkajflkjldaflkajldjflkaj");
//                DialogDisplayer.getDefault().notifyLater(e);
            }
            
            if(saver!=null){
                saver.saveResult(result);
            }
        }
    }
}
